Skip to content

Support sessionFs in Node SDK. Update runtime.#917

Merged
SteveSandersonMS merged 18 commits intomainfrom
sdk-session-store-abstraction
Apr 1, 2026
Merged

Support sessionFs in Node SDK. Update runtime.#917
SteveSandersonMS merged 18 commits intomainfrom
sdk-session-store-abstraction

Conversation

@SteveSandersonMS
Copy link
Copy Markdown
Contributor

@SteveSandersonMS SteveSandersonMS commented Mar 24, 2026

Adds support for virtualizing the per-session storage that the underlying runtime uses. This includes the event log and large output handling. It doesn't yet include other workspace files.

Currently, client support is in the Node SDK only but will be added to others soon.

@SteveSandersonMS SteveSandersonMS force-pushed the sdk-session-store-abstraction branch 2 times, most recently from 5b7cfff to 152e970 Compare March 25, 2026 15:38
@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

I've reviewed this PR for consistency with the other SDK implementations (Python, Go, and .NET). This PR introduces a custom session data store feature to the Node.js/TypeScript SDK only.

Summary of Changes

This PR adds:

  • SessionDataStoreConfig interface with a descriptor field and handler methods (load, append, truncate, list, delete)
  • New sessionDataStore option in CopilotClientOptions
  • Client-side RPC handlers via registerClientApiHandlers() that delegate storage operations to user-provided callbacks
  • Automatic registration of the data store when sessionDataStore is provided during connection
  • Test coverage in nodejs/test/e2e/session_store.test.ts

Consistency Gap Identified

Finding: This feature is not yet implemented in Python, Go, or .NET SDKs.

The equivalent configuration options in other SDKs are:

  • Python: SubprocessConfig and ExternalServerConfig (in python/copilot/client.py)
  • Go: ClientOptions struct (in go/types.go)
  • .NET: CopilotClientOptions class (in dotnet/src/Types.cs)

None of these currently have a sessionDataStore / session_data_store / SessionDataStore option.

Recommendation

Since this PR is marked as Draft and the body says "Not yet ready," I suggest:

  1. Consider this a phased rollout: If the goal is to validate the API design in Node.js first before implementing in other languages, that's a reasonable approach. Just ensure there's a plan to bring feature parity to other SDKs.

  2. For a consistent multi-language release, you may want to:

    • Add equivalent session_data_store support to Python (SubprocessConfig / client constructor)
    • Add SessionDataStore field to Go's ClientOptions struct
    • Add SessionDataStore property to .NET's CopilotClientOptions class
    • Ensure the handler interface (load, append, truncate, list, delete) is similarly structured across languages (accounting for naming conventions)
  3. API naming patterns to maintain:

    • Node.js: sessionDataStore (camelCase) ✅
    • Python: session_data_store (snake_case)
    • Go: SessionDataStore (PascalCase for public fields)
    • .NET: SessionDataStore (PascalCase)

Conclusion

No immediate action required since this is a draft PR. However, before marking this feature as generally available, consider implementing equivalent functionality in Python, Go, and .NET to maintain feature parity across the SDK suite. If this is intended as a Node.js-exclusive feature, that should be clearly documented with rationale.

Great work on the test coverage and RPC handler design! The approach is clean and extensible. 👍

Generated by SDK Consistency Review Agent for issue #917 ·

@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

I've reviewed this PR for consistency across the four SDK implementations (Node.js/TypeScript, Python, Go, and .NET).

Summary

This PR introduces a custom session data store feature to the Node.js/TypeScript SDK only. This creates a feature parity gap with the other SDK implementations.

What This PR Adds

The PR adds a sessionDataStore option to CopilotClientOptions that allows applications to provide custom storage backends for session event persistence. Key aspects:

  1. New client option: sessionDataStore?: SessionDataStoreConfig in CopilotClientOptions
  2. Handler interface: SessionDataStoreHandler with methods: load(), append(), truncate(), list(), delete()
  3. Client API RPC registration: New registerClientApiHandlers() function that sets up bi-directional RPC handlers
  4. Automatic registration: When configured, the client calls sessionDataStore.setDataStore on connection and registers RPC handlers for the CLI to invoke

Cross-SDK Status

SDK Custom Session Store Support
Node.js/TypeScript ✅ Added in this PR
Python ❌ Not implemented
Go ❌ Not implemented
.NET ❌ Not implemented

Recommendation

To maintain feature parity, this capability should be added to the other three SDKs. Here's how it would map to each language's conventions:

Python (python/copilot/client.py)

  • Add session_data_store?: SessionDataStoreConfig to connection config (matching snake_case convention)
  • Create SessionDataStoreHandler protocol/interface with async methods
  • Example:
`@dataclass`
class SessionDataStoreConfig:
    descriptor: str
    load: Callable[[SessionDataStoreLoadParams], Awaitable[SessionDataStoreLoadResult]]
    append: Callable[[SessionDataStoreAppendParams], Awaitable[None]]
    truncate: Callable[[SessionDataStoreTruncateParams], Awaitable[SessionDataStoreTruncateResult]]
    list: Callable[[], Awaitable[SessionDataStoreListResult]]
    delete: Callable[[SessionDataStoreDeleteParams], Awaitable[None]]

Go (go/client.go, go/types.go)

  • Add SessionDataStore *SessionDataStoreConfig to ClientOptions (matching Go naming)
  • Create SessionDataStoreHandler interface
  • Example:
type SessionDataStoreHandler interface {
    Load(ctx context.Context, params SessionDataStoreLoadParams) (*SessionDataStoreLoadResult, error)
    Append(ctx context.Context, params SessionDataStoreAppendParams) error
    Truncate(ctx context.Context, params SessionDataStoreTruncateParams) (*SessionDataStoreTruncateResult, error)
    List(ctx context.Context) (*SessionDataStoreListResult, error)
    Delete(ctx context.Context, params SessionDataStoreDeleteParams) error
}

type SessionDataStoreConfig struct {
    Descriptor string
    Handler    SessionDataStoreHandler
}

.NET (dotnet/src/Types.cs, dotnet/src/Client.cs)

  • Add SessionDataStore property to CopilotClientOptions (PascalCase)
  • Create ISessionDataStoreHandler interface or callback delegates
  • Example:
public interface ISessionDataStoreHandler
{
    Task(SessionDataStoreLoadResult) LoadAsync(SessionDataStoreLoadParams parameters, CancellationToken cancellationToken = default);
    Task AppendAsync(SessionDataStoreAppendParams parameters, CancellationToken cancellationToken = default);
    Task(SessionDataStoreTruncateResult) TruncateAsync(SessionDataStoreTruncateParams parameters, CancellationToken cancellationToken = default);
    Task(SessionDataStoreListResult) ListAsync(CancellationToken cancellationToken = default);
    Task DeleteAsync(SessionDataStoreDeleteParams parameters, CancellationToken cancellationToken = default);
}

public sealed class SessionDataStoreConfig
{
    public required string Descriptor { get; init; }
    public required ISessionDataStoreHandler Handler { get; init; }
}

Notes

  • This is marked as a draft PR with "Not yet ready" in the description, so it may still be in development
  • The feature is valuable for applications that need custom storage (e.g., Redis, PostgreSQL, cloud storage)
  • Given that this is a draft, consider whether implementation should happen across all SDKs before merging, or if language-specific rollout is acceptable
  • The E2E tests added (nodejs/test/e2e/session_store.test.ts) demonstrate the feature thoroughly, which will be helpful when implementing in other languages

Would you like me to create tracking issues for the other SDK implementations, or is this intentionally a Node.js-only feature for now?

Generated by SDK Consistency Review Agent for issue #917 ·

@SteveSandersonMS SteveSandersonMS force-pushed the sdk-session-store-abstraction branch from 152e970 to 3d11cff Compare March 25, 2026 15:45
@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

This PR introduces a significant new feature: custom session data storage backend support via the sessionDataStore option. However, this feature is currently only implemented in the Node.js/TypeScript SDK.

What This PR Adds (Node.js/TypeScript only)

  1. New client option: sessionDataStore in CopilotClientOptions that allows providing custom callbacks for:

    • load(sessionId) - Load session events from storage
    • append(sessionId, events) - Append new events to storage
    • truncate(sessionId, upToEventId) - Truncate old events (compaction)
    • list() - List all sessions in storage
    • delete(sessionId) - Delete a session from storage
  2. Generated RPC types: New SessionDataStore* types in generated/rpc.ts including handler interfaces and registration helpers

  3. Integration: The client automatically calls sessionDataStore.setDataStore on connection and registers RPC handlers to route storage operations to the provided callbacks

  4. E2E tests: Comprehensive test suite demonstrating the feature with an in-memory store implementation

Missing From Other SDKs

Python (python/copilot/)

  • ❌ No session_data_store option in client configuration
  • ❌ No equivalent handler interface or callbacks
  • ❌ No tests for this feature

Go (go/)

  • ❌ No SessionDataStore field in ClientOptions
  • ❌ No equivalent handler interface or callbacks
  • ❌ No tests for this feature

.NET (dotnet/src/)

  • ❌ No SessionDataStore property in CopilotClientOptions
  • ❌ No equivalent handler interface or callbacks
  • ❌ No tests for this feature

Recommendation

This is a valuable feature for users who want to:

  • Store session state in Redis, databases, or other persistence layers
  • Implement custom retention/archival policies
  • Share session state across distributed systems

To maintain SDK consistency, this feature should be implemented across all four SDKs with equivalent APIs (accounting for language conventions):

  • Python: session_data_store parameter with Protocol or ABC defining required methods
  • Go: SessionDataStore field in ClientOptions with interface type defining required methods
  • .NET: SessionDataStore property in CopilotClientOptions with interface defining required methods

Each SDK should follow the same pattern established here:

  1. Client option to provide storage handlers
  2. Automatic registration on connection via sessionDataStore.setDataStore RPC
  3. Handler callbacks invoked by the CLI server for persistence operations
  4. E2E tests with an in-memory store implementation

Note: This PR is marked as draft and states "Not yet ready", so there may be time to implement this feature across all SDKs before merging. If the intent is to ship this feature incrementally (Node.js first), please add tracking issues for the other SDKs and update their respective roadmaps/documentation to note the feature gap.

Generated by SDK Consistency Review Agent for issue #917 ·

@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review: Session Filesystem Provider

This PR introduces a new session filesystem provider feature to the Node.js/TypeScript SDK only. This represents a consistency gap across the SDK implementations.

Feature Summary

The PR adds the ability to provide a custom storage backend for session state via the sessionFs option in CopilotClientOptions. This allows applications to:

  • Intercept all file I/O operations for session state (events, workspace, checkpoints)
  • Implement custom storage backends (e.g., cloud storage, databases, virtual filesystems)
  • Control where and how session data is persisted

Added APIs:

  • CopilotClientOptions.sessionFs?: SessionFsConfig - Configuration property
  • SessionFsConfig interface extending SessionFsHandler with initialCwd, sessionStatePath, and conventions
  • SessionFsHandler interface with methods: readFile, writeFile, appendFile, exists, stat, mkdir, readdir, rm, rename
  • Client-side RPC handler registration via registerClientApiHandlers

Cross-SDK Consistency Analysis

Current state:

  • Node.js/TypeScript: Full implementation (this PR)
  • Python: No equivalent feature
  • Go: No equivalent feature
  • .NET: No equivalent feature

Recommendation

This is a useful extensibility feature that would benefit all SDK implementations. Consider whether:

  1. This feature should be added to other SDKs to maintain feature parity, especially for:

    • Applications needing cloud-based session storage
    • Multi-tenant scenarios with isolated storage
    • Testing scenarios with mock filesystems (note: the test uses @platformatic/vfs)
  2. Language-specific alternatives might be more idiomatic:

    • Python: Could use protocol classes or abstract base classes
    • Go: Could use interface types (already idiomatic)
    • .NET: Could use interface types with async methods
  3. Documentation should clarify this is currently Node.js-only if other SDKs won't implement it soon

Implementation Notes (if extending to other SDKs)

The feature requires:

  • Client-side JSON-RPC request handlers (reverse RPC from server → client)
  • Async file operation interfaces matching the 9 methods in SessionFsHandler
  • Registration call (sessionFs.setProvider) sent after connection establishment
  • Path convention awareness (Windows vs. Linux)

Python equivalent naming:

class SessionFsHandler(Protocol):
    async def read_file(self, params: SessionFsReadFileParams) -> SessionFsReadFileResult: ...
    async def write_file(self, params: SessionFsWriteFileParams) -> None: ...
    # ... etc

Go equivalent naming:

type SessionFsHandler interface {
    ReadFile(ctx context.Context, params SessionFsReadFileParams) (SessionFsReadFileResult, error)
    WriteFile(ctx context.Context, params SessionFsWriteFileParams) error
    // ... etc
}

.NET equivalent naming:

public interface ISessionFsHandler {
    Task(SessionFsReadFileResult) ReadFileAsync(SessionFsReadFileParams params, CancellationToken cancellationToken = default);
    Task WriteFileAsync(SessionFsWriteFileParams params, CancellationToken cancellationToken = default);
    // ... etc
}

Status: This PR is architecturally sound for the Node.js SDK. The consistency gap is flagged for awareness and future planning, not as a blocker for this PR.

Generated by SDK Consistency Review Agent for issue #917 ·

@paulpach
Copy link
Copy Markdown

paulpach commented Mar 29, 2026

I am very interested in this feature. We would love to use something like redis to store the session data so that I can use this in a multi instance deployment.

Providing a virtual file system abstraction instead of session data storage abstraction (as originally coded) is a bit strange to me. It is awkward to map to something like redis or mongo which are not structured like filesystems. In the commit, copilot-agent-runtime#5432 is mentioned, but there is no link, could you provide a link to that discussion? is that public?

@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

This PR continues to add session filesystem provider support exclusively to the Node.js/TypeScript SDK.

Previous reviews have thoroughly documented this consistency gap (see March 27 review). The feature remains unimplemented in Python, Go, and .NET.

Latest Changes (March 30)

The most recent commit ("Expand API surface slightly") made minor additions to the generated RPC types and TypeScript types—no cross-SDK implications.

Key Observations

  1. User interest: A community member expressed interest in using this for distributed deployments with Redis backing.

  2. Feature scope: This provides filesystem-level abstraction (9 methods: readFile, writeFile, appendFile, exists, stat, mkdir, readdir, readdirWithTypes, rm, rename) rather than higher-level session storage.

  3. Draft status: PR remains marked as draft, allowing time to plan cross-SDK rollout strategy.

Recommendation

Given external user interest and the value for distributed/cloud scenarios, consider the rollout strategy:

  • Option A: Ship Node.js first to validate the API design, then implement in other SDKs
  • Option B: Implement across all SDKs before merging (maintaining strict feature parity)

If choosing Option A, document the feature gap in other SDK READMEs and create tracking issues for Python/Go/.NET implementations.


No new consistency issues introduced since the last review. The core consistency gap (Node.js-only feature) remains.

Generated by SDK Consistency Review Agent for issue #917 ·

@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review: Session Filesystem Provider

This PR introduces a custom session filesystem provider feature that allows Node.js SDK users to provide their own storage implementation for session-scoped files, routing all file I/O through custom callbacks instead of the server's default local filesystem.

✅ Current Implementation (Node.js only)

The PR adds:

  • SessionFsConfig interface with:
    • initialCwd: Initial working directory
    • sessionStatePath: Path for session-scoped files
    • conventions: "windows" | "linux" path conventions
    • createHandler: Factory function to create a SessionFsHandler per session
  • SessionFsHandler interface with methods like readFile, writeFile, mkdir, readdir, etc.
  • RPC integration via sessionFs.setProvider and client-side handlers for sessionFs.* RPC calls
  • Registration in CopilotClient constructor via sessionFs option

⚠️ Cross-SDK Consistency Gap

This feature does NOT exist in Python, Go, or .NET SDKs. All three SDKs:

  • Support infinite sessions with workspace persistence (via InfiniteSessionConfig)
  • Expose workspacePath / WorkspacePath property on sessions
  • Provide session.workspace.* RPC methods (listFiles, readFile, createFile)
  • Do NOT support custom storage providers - storage is always managed by the CLI server

🤔 Assessment

This appears to be an intentional Node.js-specific enhancement rather than an oversight, because:

  1. Language ecosystem alignment: Node.js has a rich ecosystem of virtual filesystem libraries (e.g., @platformatic/vfs added as a devDependency in this PR), making custom storage providers natural in JavaScript/TypeScript environments

  2. Platform requirements: Some Node.js hosting environments (serverless, edge functions) may not have reliable local filesystem access, making custom providers essential

  3. Implementation complexity: This feature requires:

    • Bidirectional RPC routing (server calls back into client)
    • Per-session handler lifecycle management
    • Protocol negotiation for provider registration

    Adding this to all SDKs is non-trivial

📋 Recommendation

If this feature is intended for Node.js only:

  • ✅ Document this as a Node.js-specific capability in the README
  • ✅ Consider whether Python, Go, and .NET will need this in the future (e.g., for cloud/serverless scenarios)

If this should be available in all SDKs:

  • ❌ This PR should not be merged until equivalent implementations exist in Python, Go, and .NET
  • ❌ Coordinate with maintainers to plan implementation timeline for other languages

Suggested documentation addition (if Node.js-only is intentional):

### Custom Session Filesystem Provider (Node.js only)

For environments without reliable local filesystem access, you can provide a custom storage backend:

```typescript
const client = new CopilotClient({
  sessionFs: {
    initialCwd: '/virtual/workspace',
    sessionStatePath: '.copilot/sessions',
    conventions: 'linux',
    createHandler: (session) => ({
      readFile: async ({ path }) => { /* ... */ },
      writeFile: async ({ path, content }) => { /* ... */ },
      // ... other methods
    })
  }
});
```

cc: @SteveSandersonMS - Is this intended as a Node.js-specific feature, or should we coordinate adding it to Python/Go/.NET as well?

Generated by SDK Consistency Review Agent for issue #917 ·

@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

This PR adds support for custom session filesystem providers to the Node.js/TypeScript SDK only, creating a feature gap across the multi-language SDK implementations.

What's Added (Node.js SDK)

The PR introduces:

  1. CopilotClientOptions.sessionFs - Connection-level configuration with:

    • initialCwd: Working directory for sessions
    • sessionStatePath: Path where runtime stores session files
    • conventions: Path conventions ("windows" or "linux")
  2. SessionConfig.createSessionFsHandler - Per-session handler factory returning a SessionFsHandler with methods for file operations:

    • readFile(), writeFile(), appendFile()
    • exists(), stat(), mkdir(), readdir(), readdirWithTypes()
    • rm(), rename()
  3. RPC integration - Registers sessionFs.setProvider on connection and routes sessionFs.* requests to session-specific handlers

Feature Parity Status

SDK Custom Session FS Support
Node.js/TypeScript ✅ Added in this PR
Python ❌ Not implemented
Go ❌ Not implemented
.NET ❌ Not implemented

Recommendation

This appears to be an infrastructure feature that enables advanced use cases like:

  • Custom storage backends (cloud storage, databases, etc.)
  • Virtual filesystems for testing
  • Cross-platform path handling

Should this feature be available in all SDKs? Consider:

  1. If yes - Plan follow-up PRs to add equivalent functionality to Python (session_fs config + handler protocol), Go (SessionFsConfig + SessionFsHandler interface), and .NET (SessionFsConfig + ISessionFsHandler interface)

  2. If no (Node.js-specific) - Document why this is TypeScript-only (e.g., specific integration requirements, experimental feature)

  3. If deferred - Add a TODO comment in other SDKs' client files noting this feature exists in Node.js for future parity

Since this is marked as a draft PR, this may be an experimental feature. If it moves toward merge, I recommend creating tracking issues for the other SDKs to maintain consistency.


Note: No inline comments added since this is a new feature addition (not modifying existing code). The implementation looks well-structured with proper RPC handler registration via registerClientSessionApiHandlers().

Generated by SDK Consistency Review Agent for issue #917 ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Cross-SDK Consistency Review

This PR adds a significant new feature to the Node.js/TypeScript SDK only: Custom Session Filesystem Provider.

What's Added (Node.js SDK)

This PR introduces the ability for applications to provide their own filesystem implementation for session-scoped storage:

  1. CopilotClientOptions.sessionFs - Client-level configuration specifying:

    • initialCwd - working directory for sessions
    • sessionStatePath - where session state files are stored
    • conventions - path conventions (windows/posix)
  2. SessionConfig.createSessionFsHandler - Per-session callback that returns a SessionFsHandler interface implementing:

    • readFile, writeFile, appendFile
    • exists, stat, mkdir, readdir, readdirWithTypes
    • rm, rename
  3. RPC integration - The client registers these handlers via registerClientSessionApiHandlers() so the CLI server can call back to the client for filesystem operations.

Impact on Other SDKs

Status: ❌ This feature does NOT exist in Python, Go, or .NET SDKs

This creates a feature parity gap across the SDK implementations. Applications using other language SDKs cannot:

  • Provide custom filesystem implementations for session storage
  • Store session data in cloud storage, databases, or in-memory stores
  • Control where/how session state is persisted

Recommendation

Consider whether this feature should be added to the other SDKs to maintain cross-language consistency. This is particularly important because:

  1. Architectural impact: This is a foundational extensibility point that affects how sessions persist state
  2. Use case portability: Applications may want to use the same custom storage backend across different language implementations
  3. Documentation: If this remains Node-only, it should be clearly documented as a language-specific feature

Suggested Path Forward

If this feature should be available across all SDKs (which I recommend given its architectural significance):

  • Python SDK: Add session_fs config to SubprocessConfig/ExternalServerConfig and create_session_fs_handler callback to session creation
  • Go SDK: Add SessionFs field to ClientOptions and CreateSessionFsHandler to SessionConfig
  • .NET SDK: Add SessionFs property to CopilotClientOptions and CreateSessionFsHandler delegate to SessionConfig

The RPC types are likely already generated in other languages from the same schema, so the main work would be wiring up the client-side registration and handler interfaces.


Note: This is marked as a Draft PR, so this may already be on the roadmap. If cross-SDK support is planned, consider implementing it before finalizing to avoid releasing a Node-only API that creates expectations.

Generated by SDK Consistency Review Agent for issue #917 ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by SDK Consistency Review Agent for issue #917

* on connection, routing all session-scoped file I/O through these callbacks
* instead of the server's default local filesystem storage.
*/
sessionFs?: SessionFsConfig;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency: This new client-level option does not exist in Python, Go, or .NET SDKs.

Equivalent locations in other SDKs:

  • Python: Would need to be added to SubprocessConfig/ExternalServerConfig in python/copilot/client.py
  • Go: Would need to be added to ClientOptions struct in go/types.go
  • .NET: Would need to be added to CopilotClientOptions class in dotnet/src/Types.cs

* Supplies a handler for session filesystem operations. This takes effect
* only if {@link CopilotClientOptions.sessionFs} is configured.
*/
createSessionFsHandler?: (session: CopilotSession) => SessionFsHandler;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency: This session-level callback does not exist in Python, Go, or .NET SDKs.

Equivalent locations in other SDKs:

  • Python: Would need to be added to the create_session() method parameters in python/copilot/client.py
  • Go: Would need to be added to SessionConfig struct in go/types.go
  • .NET: Would need to be added to SessionConfig class in dotnet/src/Types.cs

The handler interface (SessionFsHandler) would need similar language-specific definitions.

@@ -399,6 +404,15 @@ export class CopilotClient {
// Verify protocol version compatibility
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency: This registration logic for the session filesystem provider does not exist in other SDKs.

Equivalent locations:

  • Python: Would need RPC call in _connect() method in python/copilot/client.py
  • Go: Would need RPC call in Connect() method in go/client.go
  • .NET: Would need RPC call in ConnectAsync() method in dotnet/src/Client.cs

@@ -1562,6 +1596,14 @@ export class CopilotClient {
await this.handleSystemMessageTransform(params)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency: This handler registration pattern (registerClientSessionApiHandlers) does not exist in other SDKs.

Equivalent locations:

  • Python: Would need similar RPC handler registration in _connect() in python/copilot/client.py
  • Go: Would need similar handler registration in Connect() in go/client.go
  • .NET: Would need similar handler registration in ConnectAsync() in dotnet/src/Client.cs

The generated RPC files (python/copilot/generated/rpc.py, go/rpc/rpc.go, dotnet/src/Generated/Rpc.cs) likely already have the type definitions for these methods if they're code-generated from the same schema.

}

export interface McpConfigListResult {
/**
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency: The SessionFsHandler interface and related types (like SessionFsReadFileParams, etc.) are currently only in the Node.js generated RPC file.

Code generation impact: When this feature is added to other SDKs, the RPC schema will need to be updated first, which will then generate the corresponding types in:

  • python/copilot/generated/rpc.py (Python)
  • go/rpc/generated_rpc.go (Go)
  • dotnet/src/Generated/Rpc.cs (.NET)

Currently, searching these files shows no sessionFs/SessionFs types, confirming they haven't been generated yet for other languages.

@SteveSandersonMS SteveSandersonMS changed the title Draft: Support providing a custom session store Support sessionFs in Node SDK. Update runtime. Apr 1, 2026
SteveSandersonMS and others added 15 commits April 1, 2026 14:00
- Add sessionDataStore option to CopilotClientOptions
- Extend codegen to generate client API handler types (SessionDataStoreHandler)
- Register as session data storage provider on connection via sessionDataStore.setDataStore RPC
- Add E2E tests for persist, resume, list, delete, and reject scenarios

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Migrate the TypeScript SDK from the event-level sessionDataStore
abstraction to the general-purpose SessionFs virtual filesystem,
matching the runtime's new design (copilot-agent-runtime#5432).

Key changes:
- Regenerate RPC types from runtime schema with sessionFs.* methods
- Replace SessionDataStoreConfig with SessionFsConfig (initialCwd,
  sessionStatePath, conventions + 9 filesystem handler callbacks)
- Client calls sessionFs.setProvider on connect (was setDataStore)
- Client registers sessionFs.* RPC handlers (readFile, writeFile,
  appendFile, exists, stat, mkdir, readdir, rm, rename)
- New E2E tests with InMemorySessionFs (filesystem-level, not events)
- Remove old session_store tests and snapshots
@SteveSandersonMS SteveSandersonMS force-pushed the sdk-session-store-abstraction branch from a482499 to 45b44c7 Compare April 1, 2026 13:03
@SteveSandersonMS SteveSandersonMS force-pushed the sdk-session-store-abstraction branch from e534ed5 to a938ece Compare April 1, 2026 13:09
@SteveSandersonMS SteveSandersonMS marked this pull request as ready for review April 1, 2026 13:10
@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner April 1, 2026 13:10
Copilot AI review requested due to automatic review settings April 1, 2026 13:10
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Cross-SDK Consistency Review: sessionFs Feature

Thank you for implementing the sessionFs feature! I've reviewed PR #917 for cross-language consistency across our four SDK implementations.

Summary

This PR adds support for virtualizing per-session storage (event logs, large output handling, etc.) through a custom filesystem provider. The implementation is currently Node.js/TypeScript only, as noted in the PR description: "Currently, client support is in the Node SDK only but will be added to others soon."

What Was Added (Node.js SDK)

Client-level configuration:

  • CopilotClientOptions.sessionFs?: SessionFsConfig - Connection-level config with initialCwd, sessionStatePath, and conventions (windows/posix)
  • sessionFs.setProvider RPC call during client connection

Session-level integration:

  • SessionConfig.createSessionFsHandler?: (session: CopilotSession) => SessionFsHandler
  • Handler registration for incoming RPC calls from the server (sessionFs.readFile, sessionFs.writeFile, etc.)
  • SessionFsHandler interface with 10 filesystem operations (readFile, writeFile, appendFile, exists, stat, mkdir, readdir, readdirWithTypes, rm, rename)

Generated types (all SDKs):

  • SessionFsSetProviderRequest/Result types
  • SessionFsHandler operation param/result types
  • These were auto-generated and propagated to Python, Go, and .NET

Testing:

  • Comprehensive E2E tests using @platformatic/vfs memory provider
  • Test snapshots validating the RPC protocol

Current State: Feature Parity Gap

SDK Generated Types Client Options Session Config Handler Registration Status
Node.js sessionFs createSessionFsHandler ✅ Auto-registered Complete
Python Not implemented
Go Not implemented
.NET Not implemented

Recommendations

This PR is fine to merge since:

  1. The PR description explicitly acknowledges this is Node.js-only for now
  2. Generated types are already in place for future implementations
  3. The feature doesn't break existing functionality in other SDKs

📋 For follow-up work, the other SDKs will need:

Python SDK (python/copilot/client.py, python/copilot/session.py):

  • Add session_fs: SessionFsConfig | None to SubprocessConfig/ExternalServerConfig
  • Add create_session_fs_handler: Callable[[CopilotSession], SessionFsHandler] | None to create_session/resume_session methods
  • Call sessionFs.setProvider RPC during connection when configured
  • Register incoming RPC handlers (sessionFs.readFile, etc.) and route to session-specific handler

Go SDK (go/types.go, go/client.go, go/session.go):

  • Add SessionFs *SessionFsConfig to ClientOptions
  • Add CreateSessionFsHandler func(*Session) SessionFsHandler to SessionConfig
  • Implement RPC handler registration using jsonrpc2.HandlerWithError
  • Call sessionFs.setProvider during Start()

.NET SDK (dotnet/src/Types.cs, dotnet/src/Client.cs, dotnet/src/Session.cs):

  • Add SessionFsConfig? SessionFs property to CopilotClientOptions
  • Add Func(CopilotSession, ISessionFsHandler)? CreateSessionFsHandler to SessionConfig/ResumeSessionConfig
  • Define ISessionFsHandler interface mirroring the generated types
  • Register JSON-RPC handlers for incoming sessionFs.* calls
  • Call sessionFs.setProvider in StartAsync()

API Design Consistency Note

The Node.js implementation follows excellent patterns that should be preserved when implementing in other languages:

  1. Two-level configuration: Connection-level config (sessionFs) + session-level handler factory (createSessionFsHandler)
  2. Lazy handler creation: Handlers are created per-session, allowing session-specific context
  3. Type safety: The SessionFsHandler interface is generated, ensuring protocol consistency
  4. Error handling: Missing handler throws clear errors when sessionFs is enabled

When implementing in other SDKs, please maintain these design principles while adapting to language idioms:

  • Python: Use Protocol or ABC for SessionFsHandler interface, snake_case method names
  • Go: Define interface with PascalCase methods, use context parameters
  • .NET: Use ISessionFsHandler interface, PascalCase methods, Task(T) return types

Conclusion: ✅ No blocking issues for this PR. The generated types ensure the other SDKs can implement this feature consistently when ready. Consider opening follow-up issues to track implementation in Python, Go, and .NET to maintain feature parity.

Generated by SDK Consistency Review Agent for issue #917 ·

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Node.js SDK support for session-scoped filesystem virtualization (“sessionFs”) so the runtime can route per-session storage (event log, large tool outputs, compaction artifacts) through client-provided callbacks, alongside a runtime/schema update and new E2E coverage.

Changes:

  • Add sessionFs client option + per-session createSessionFsHandler wiring and JSON-RPC handler registration in the Node SDK.
  • Update codegen to support nullable/void RPC results and generate client-session RPC handler registration (TS), plus regenerate RPC/session-event artifacts across languages.
  • Add Node E2E tests + replay snapshots; improve snapshot normalization for large-output temp file paths.

Reviewed changes

Copilot reviewed 19 out of 26 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/snapshots/session_lifecycle/should_support_multiple_concurrent_sessions.yaml Updates concurrency lifecycle snapshot ordering/content.
test/snapshots/session_fs/should_succeed_with_compaction_while_using_sessionfs.yaml New replay snapshot for compaction while using sessionFs.
test/snapshots/session_fs/should_route_file_operations_through_the_session_fs_provider.yaml New replay snapshot validating file I/O goes through sessionFs.
test/snapshots/session_fs/should_reject_setprovider_when_sessions_already_exist.yaml New replay snapshot for provider-set rejection scenario.
test/snapshots/session_fs/should_map_large_output_handling_into_sessionfs.yaml New replay snapshot for large tool output temp-file behavior.
test/snapshots/session_fs/should_load_session_data_from_fs_provider_on_resume.yaml New replay snapshot validating resume loads from provider.
test/harness/replayingCapiProxy.ts Adds default tool-result normalization for large-output temp filenames and wildcard normalizers.
scripts/codegen/utils.ts Extends API schema typing (adds clientSession, makes result nullable).
scripts/codegen/typescript.ts Generates void results, includes clientSession RPCs, and emits handler registration helpers.
scripts/codegen/csharp.ts Adjusts enum type resolution in generated method signatures.
python/copilot/generated/session_events.py Regenerates events (adds aborted, adds needs-auth status).
python/copilot/generated/rpc.py Regenerates RPC types (adds MCP config + sessionFs setProvider types, status updates).
nodejs/test/e2e/session_fs.test.ts New E2E suite validating sessionFs routing, resume behavior, large output, and compaction.
nodejs/test/e2e/harness/sdkTestContext.ts Allows passing through CopilotClientOptions into the E2E harness.
nodejs/src/types.ts Adds sessionFs option and createSessionFsHandler session config typing + exports.
nodejs/src/session.ts Adds clientSessionApis container for per-session client RPC handlers.
nodejs/src/index.ts Re-exports SessionFsConfig and SessionFsHandler.
nodejs/src/generated/session-events.ts Regenerated TS events (adds aborted, adds needs-auth to statuses).
nodejs/src/generated/rpc.ts Regenerated RPC types + server mcp/sessionFs APIs + client-session handler registration.
nodejs/src/client.ts Registers provider on connect; wires per-session handler into dispatch; registers client-session RPC handlers.
nodejs/package.json Bumps @github/copilot runtime and adds @platformatic/vfs devDependency for tests.
nodejs/package-lock.json Lockfile updates for runtime bump and new dev dependency.
go/rpc/generated_rpc.go Regenerated Go RPC types (adds MCP/sessionFs + status updates).
go/generated_session_events.go Regenerated Go events (adds aborted, adds needs-auth).
dotnet/src/Generated/SessionEvents.cs Regenerated .NET events (adds aborted, adds needs-auth).
dotnet/src/Generated/Rpc.cs Regenerated .NET RPC types (adds sessionFs types, adds MCP placeholder API).
Files not reviewed (1)
  • nodejs/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)

nodejs/src/client.ts:825

  • Same leak as createSession: if sessionFs is enabled and createSessionFsHandler is missing, this throws after adding the session to this.sessions. Either validate before registering the session or remove it from the map before throwing.
        this.sessions.set(sessionId, session);
        if (this.sessionFsConfig) {
            if (config.createSessionFsHandler) {
                session.clientSessionApis.sessionFs = config.createSessionFsHandler(session);
            } else {
                throw new Error(
                    "createSessionFsHandler is required in session config when sessionFs is enabled in client options."
                );
            }

Comment on lines 682 to +690
this.sessions.set(sessionId, session);
if (this.sessionFsConfig) {
if (config.createSessionFsHandler) {
session.clientSessionApis.sessionFs = config.createSessionFsHandler(session);
} else {
throw new Error(
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
);
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When sessionFs is enabled and createSessionFsHandler is missing, this throws after the session has already been added to this.sessions, leaving a leaked/half-initialized session in the map. Validate createSessionFsHandler before this.sessions.set(...), or ensure you this.sessions.delete(sessionId) before throwing so the client state stays consistent.

This issue also appears on line 817 of the same file.

Suggested change
this.sessions.set(sessionId, session);
if (this.sessionFsConfig) {
if (config.createSessionFsHandler) {
session.clientSessionApis.sessionFs = config.createSessionFsHandler(session);
} else {
throw new Error(
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
);
}
if (this.sessionFsConfig && !config.createSessionFsHandler) {
throw new Error(
"createSessionFsHandler is required in session config when sessionFs is enabled in client options."
);
}
this.sessions.set(sessionId, session);
if (this.sessionFsConfig && config.createSessionFsHandler) {
session.clientSessionApis.sessionFs = config.createSessionFsHandler(session);

Copilot uses AI. Check for mistakes.
"zod": "^4.3.6"
},
"devDependencies": {
"@platformatic/vfs": "^0.3.0",
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@platformatic/vfs@^0.3.0 declares engines: { node: ">= 22" } (visible in the updated package-lock), but this package’s engines.node is >=20.0.0. Adding a devDependency that requires Node 22 can make npm install / tests fail for contributors on Node 20/21. Consider using a vfs library compatible with Node >=20, pinning to a compatible version, or replacing it with a small in-test in-memory fs implementation.

Suggested change
"@platformatic/vfs": "^0.3.0",
"@platformatic/vfs": "0.2.0",

Copilot uses AI. Check for mistakes.
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import { SessionCompactionCompleteEvent } from "@github/copilot/sdk";
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import is only used as a TypeScript type annotation. Use a type-only import (import type { ... }) to avoid pulling @github/copilot/sdk into the runtime module graph during tests and to keep emitted JS clean.

Suggested change
import { SessionCompactionCompleteEvent } from "@github/copilot/sdk";
import type { SessionCompactionCompleteEvent } from "@github/copilot/sdk";

Copilot uses AI. Check for mistakes.
useStdio: false, // Use TCP so we can connect from a second client
env,
});
await client.createSession({ onPermissionRequest: approveAll, createSessionFsHandler });
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The should reject setProvider... test creates a new CopilotClient (useStdio: false) but never stops it, and it also creates a session without disconnecting it. This can leak a CLI process/socket across tests and cause flakiness. Add onTestFinished(() => client.forceStop()) (or await client.stop() in a finally) and ensure the created session is disconnected.

Suggested change
await client.createSession({ onPermissionRequest: approveAll, createSessionFsHandler });
onTestFinished(() => client.forceStop());
const session = await client.createSession({ onPermissionRequest: approveAll, createSessionFsHandler });
onTestFinished(async () => {
await session.disconnect();
});

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +89
const client = new CopilotClient({
useStdio: false, // Use TCP so we can connect from a second client
env,
});
await client.createSession({ onPermissionRequest: approveAll, createSessionFsHandler });

// Get the port the first client's runtime is listening on
const port = (client as unknown as { actualPort: number }).actualPort;

// Second client tries to connect with a session fs — should fail
// because sessions already exist on the runtime.
const client2 = new CopilotClient({
env,
logLevel: "error",
cliUrl: `localhost:${port}`,
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test reaches into CopilotClient’s private actualPort via a cast. That’s brittle (renames/refactors will silently break the test). Prefer exposing a small public accessor on CopilotClient for the bound address/port (even if marked @internal), or configure a known port for the runtime in the test so it doesn’t need private state.

Suggested change
const client = new CopilotClient({
useStdio: false, // Use TCP so we can connect from a second client
env,
});
await client.createSession({ onPermissionRequest: approveAll, createSessionFsHandler });
// Get the port the first client's runtime is listening on
const port = (client as unknown as { actualPort: number }).actualPort;
// Second client tries to connect with a session fs — should fail
// because sessions already exist on the runtime.
const client2 = new CopilotClient({
env,
logLevel: "error",
cliUrl: `localhost:${port}`,
// Use a shared CLI URL so both clients talk to the same runtime without
// reaching into CopilotClient's private state.
const cliUrl = env.COPILOT_TEST_CLI_URL ?? env.COPILOT_CLI_URL;
const client = new CopilotClient({
env,
logLevel: "error",
cliUrl,
});
await client.createSession({ onPermissionRequest: approveAll, createSessionFsHandler });
// Second client tries to connect with a session fs — should fail
// because sessions already exist on the runtime.
const client2 = new CopilotClient({
env,
logLevel: "error",
cliUrl,

Copilot uses AI. Check for mistakes.
);
return { entries };
},
rm: async ({ path }) => {
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm ignores the RPC params recursive and force and always calls unlink. If the runtime ever requests deleting a directory (or uses recursive: true), this handler will fail. Either implement directory removal semantics (including recursive + force) against the provider, or explicitly assert/throw with a clear error when unsupported so failures are easier to diagnose.

Suggested change
rm: async ({ path }) => {
rm: async ({ path, recursive, force }) => {
if (recursive || force) {
throw new Error(
"SessionFs rm: 'recursive' and 'force' options are not supported by the test VirtualProvider handler."
);
}

Copilot uses AI. Check for mistakes.
Comment on lines 125 to 130
export interface RpcMethod {
rpcMethod: string;
params: JSONSchema7 | null;
result: JSONSchema7;
result: JSONSchema7 | null;
stability?: string;
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RpcMethod.result is now nullable, but not all generators handle null yet. For example scripts/codegen/csharp.ts still passes method.result into emitRpcClass(...) which expects a non-null schema and will now fail typechecking (and/or at runtime) for void-returning RPCs (e.g. methods that generate Promise<void> in TS). Update the other generators to guard on method.result and emit a void/no-result method shape accordingly.

Copilot uses AI. Check for mistakes.
internal ServerMcpApi(JsonRpc rpc)
{
_rpc = rpc;
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ServerMcpApi is generated and exposed on ServerRpc, but it contains no methods. Given the TS SDK generates nested mcp.config.* RPCs, this looks like the C# RPC generator doesn’t recurse into nested server groups (e.g. mcp -> config -> list/add/...). Either update the generator to emit nested group APIs, or avoid emitting empty placeholder APIs to prevent a misleading surface area.

Suggested change
}
}
/// <summary>
/// MCP server APIs are not currently supported by this .NET SDK.
/// This method will always throw <see cref="NotSupportedException"/>.
/// </summary>
/// <param name="cancellationToken">Cancellation token (currently unused).</param>
/// <returns>A task that always faults with <see cref="NotSupportedException"/>.</returns>
public Task EnsureSupportedAsync(CancellationToken cancellationToken = default)
{
return Task.FromException(new NotSupportedException("MCP server APIs are not currently supported by the GitHub Copilot .NET SDK."));
}

Copilot uses AI. Check for mistakes.
@SteveSandersonMS SteveSandersonMS force-pushed the sdk-session-store-abstraction branch from ff25cc2 to 985543a Compare April 1, 2026 13:27
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Cross-SDK Consistency Review: sessionFs Feature

Summary

This PR introduces sessionFs support for virtualizing per-session storage, but currently only implements client-level integration in the Node.js SDK. The other three SDKs (Python, Go, .NET) only have the generated RPC types without the corresponding client configuration and handler infrastructure.

Findings

✅ Node.js SDK (Complete Implementation)

  • SessionFsConfig type added to CopilotClientOptions with initialCwd, sessionStatePath, and conventions fields
  • createSessionFsHandler added to SessionConfig and ResumeSessionConfig
  • SessionFsHandler interface with all required methods (readFile, writeFile, appendFile, exists, stat, mkdir, readdir, readdirWithTypes, rm, rename)
  • Client calls sessionFs.setProvider RPC on connection when config is present
  • Client registers session-level handlers via registerClientSessionApiHandlers

⚠️ Python SDK (Incomplete - Generated Only)

Missing from client integration:

  • No SessionFsConfig equivalent in SubprocessConfig or ExternalServerConfig
  • No create_session_fs_handler parameter in SessionConfig or ResumeSessionConfig
  • No client-side handler registration for incoming sessionFs.* RPC calls
  • Generated types exist: SessionFSSetProviderParams, SessionFSSetProviderResult, ServerSessionFsApi.set_provider()

⚠️ Go SDK (Incomplete - Generated Only)

Missing from client integration:

  • No SessionFsConfig field in ClientOptions
  • No CreateSessionFsHandler field in SessionConfig or ResumeSessionConfig
  • No client-side handler registration for incoming sessionFs.* RPC calls
  • Generated types exist: SessionFSSetProviderParams, SessionFSSetProviderResult, ServerSessionFsApi.SetProvider()

⚠️ .NET SDK (Incomplete - Generated Only)

Missing from client integration:

  • No SessionFsConfig property in CopilotClientOptions
  • No CreateSessionFsHandler property in SessionConfig or ResumeSessionConfig
  • No client-side handler registration for incoming sessionFs.* RPC calls
  • Generated types exist: SessionFsSetProviderRequest, SessionFsSetProviderResult, ServerSessionFsApi.SetProviderAsync()

Recommendation

The PR description acknowledges this: "Currently, client support is in the Node SDK only but will be added to others soon."

Suggested approach:

  1. Option A (Preferred): Hold this PR and add equivalent support to Python, Go, and .NET before merging to maintain feature parity
  2. Option B: Merge with a follow-up issue/PR tracking the missing implementations, clearly documenting in each SDK's README that sessionFs is not yet supported

For consistency, the following should be added to each SDK:

  • A SessionFsConfig equivalent in client options (matching Node's structure: initialCwd, sessionStatePath, conventions)
  • A createSessionFsHandler / CreateSessionFsHandler parameter in session configs
  • A handler interface/protocol matching Node's SessionFsHandler with all filesystem operations
  • Client logic to call sessionFs.setProvider when configured
  • Client logic to register and route incoming sessionFs.* RPC requests to the session handlers

This is a significant new feature that would benefit all SDK users equally. The generated RPC types are already in place across all languages, so the remaining work is the client-level integration layer.

Generated by SDK Consistency Review Agent for issue #917 ·

@SteveSandersonMS SteveSandersonMS merged commit 6e3d72c into main Apr 1, 2026
36 checks passed
@SteveSandersonMS SteveSandersonMS deleted the sdk-session-store-abstraction branch April 1, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants